package com.workflow.sdk.domain.factories;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.design.cfr.ChainResponsibility;
import com.design.cfr.NormalChainResponsibilityAbstract;
import com.design.cfr.model.ChainResponsibilityElementResult;
import com.design.cfr.model.ChainResponsibilityResult;
import com.design.common.ObjectUtil;
import com.workflow.sdk.domain.convert.WorkflowExecuteConverter;
import com.workflow.sdk.domain.model.entity.WorkflowExecuteEntity;
import com.workflow.sdk.domain.model.entity.dto.WorkflowExecuteDto;
import com.workflow.sdk.domain.model.idenfifer.WfExecutorIdentify;
import com.workflow.sdk.infra.common.ErrorMessage;
import com.workflow.sdk.infra.repository.factories.WorkFlowRepositoryFactory;
import com.workflow.sdk.infra.repository.model.WorkflowExecutePo;
import com.workflow.sdk.infra.repository.model.WorkflowInfoPo;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Optional;

/**
 * Work flow domain factory
 *
 * @description: workflowDo工厂
 * @author: cyj
 * @date 2022 -07-28 11:35:30
 */
public class WorkFlowDomainFactory {


    /**
     * 根据执行id获取领域对象
     *
     * @param executeId execute id
     * @return workflow execute entity
     */
    public static WorkflowExecuteEntity obtain(Long executeId) {

        WorkflowExecuteEntity workflowExecuteEntity = ObjectUtil.obtain(WorkflowExecuteEntity.class);

        // 设置领域对象参数
        // 设置所有执行对象
        workflowExecuteEntity.setWorkflowExecuteDtos(
                WorkflowExecuteConverter.INSTANCE.workflowExecutePoToDto(
                        WorkFlowRepositoryFactory.workflowExecuteRepositoryService.getWorkflowExecute(executeId)
                ));
        // 设置当前活跃对象
        workflowExecuteEntity.setActiveNode(
                workflowExecuteEntity.getWorkflowExecuteDtos().stream().filter(
                        workflowExecutePo ->
                                WorkflowExecutePo.Status.DOING.code().equals(workflowExecutePo.getStatus())).
                        findAny().orElse(null));
        // 设置标识
        workflowExecuteEntity.setIdentify(new WfExecutorIdentify());
        if (!CollectionUtils.isEmpty(workflowExecuteEntity.getWorkflowExecuteDtos())) {
            workflowExecuteEntity.getIdentify().setWorkflowId(workflowExecuteEntity.getWorkflowExecuteDtos().get(0).getWorkflowId());
            workflowExecuteEntity.getIdentify().setExecuteId(workflowExecuteEntity.getWorkflowExecuteDtos().get(0).getExecuteId());
        }
        if (null != workflowExecuteEntity.getActiveNode()) {
            Optional.ofNullable(WorkFlowRepositoryFactory.workflowInfoRepositoryService.getById(workflowExecuteEntity.getActiveNode().getWorkflowId())).ifPresent(workflowInfoPo -> {
                workflowExecuteEntity.getIdentify().setWorkflowCode(workflowInfoPo.getWorkflowCode());
            });
        }

        // 执行流程校验
        Check.checkWorkflowExecute(workflowExecuteEntity);

        return workflowExecuteEntity;
    }

    /**
     * 根据类型id获取执行对象
     *
     * @param workflowId workflow id
     * @param relevance  relevance
     * @return workflow execute entity
     */
    public static WorkflowExecuteEntity obtain(Long workflowId, String relevance) {

        WorkflowInfoPo workflowInfoPo = Optional.ofNullable(
                WorkFlowRepositoryFactory.workflowInfoRepositoryService.getById(workflowId)).orElseThrow(() -> new RuntimeException("流程配置为空！"));

        long executeId = IdWorker.getId();

        List<WorkflowExecutePo> workflowExecutePos = JSONObject.parseObject(workflowInfoPo.getWorkflowInfo(), new TypeReference<List<WorkflowExecutePo>>() {
        });

        workflowExecutePos.stream().forEach(workflowExecutePo -> {
            if (workflowExecutePo.getNodeType().equals(String.valueOf(WorkflowExecutePo.Type.start))) {
                workflowExecutePo.setStatus(WorkflowExecutePo.Status.DOING.code());
            } else {
                workflowExecutePo.setStatus(WorkflowExecutePo.Status.UN.code());
            }
            workflowExecutePo.setExecuteId(executeId);
            workflowExecutePo.setRelevance(relevance);
            workflowExecutePo.setWorkflowId(workflowInfoPo.getWorkflowId());
        });


        // 设置领域对象参数
        WorkflowExecuteEntity workflowExecuteEntity = ObjectUtil.obtain(WorkflowExecuteEntity.class);
        // 设置标识
        workflowExecuteEntity.setIdentify(new WfExecutorIdentify());
        workflowExecuteEntity.getIdentify().setWorkflowCode(workflowInfoPo.getWorkflowCode());
        // 设置所有执行对象
        workflowExecuteEntity.setWorkflowExecuteDtos(WorkflowExecuteConverter.INSTANCE.workflowExecutePoToDto(workflowExecutePos));
        // 设置活跃对象
        workflowExecuteEntity.setActiveNode(
                workflowExecuteEntity.getWorkflowExecuteDtos().stream().filter(
                        workflowExecutePo -> WorkflowExecutePo.Status.DOING.code().equals(workflowExecutePo.getStatus())).findAny().orElse(null));

        if (!CollectionUtils.isEmpty(workflowExecuteEntity.getWorkflowExecuteDtos())) {
            workflowExecuteEntity.getIdentify().setWorkflowId(workflowExecuteEntity.getWorkflowExecuteDtos().get(0).getWorkflowId());
            workflowExecuteEntity.getIdentify().setExecuteId(workflowExecuteEntity.getWorkflowExecuteDtos().get(0).getExecuteId());
        }

        // 校验
        Check.checkCreateWorkflowExecute(workflowExecuteEntity);

        return workflowExecuteEntity;

    }

    /**
     * Check
     *
     * @description:
     * @author: cyj
     * @date 2022 -08-15 15:54:49
     */
    public static class Check {

        /**
         * Check workflow execute 校验执行流程完整性
         *
         * @param workflowExecuteEntity workflow execute entity
         */
        public static void checkWorkflowExecute(WorkflowExecuteEntity workflowExecuteEntity) {
            NormalChainResponsibilityAbstract<WorkflowExecuteEntity> check = (NormalChainResponsibilityAbstract<WorkflowExecuteEntity>)
                    new NormalChainResponsibilityAbstract.Builder()
                            .add(new CheckWfEmpty())
                            .add(new CheckActiveNode()).build();
            check.start(workflowExecuteEntity).check();
        }

        /**
         * Check workflow execute 校验创建流程完成性
         *
         * @param workflowExecuteEntity workflow execute entity
         */
        public static void checkCreateWorkflowExecute(WorkflowExecuteEntity workflowExecuteEntity) {
            NormalChainResponsibilityAbstract<WorkflowExecuteEntity> check = (NormalChainResponsibilityAbstract<WorkflowExecuteEntity>)
                    new NormalChainResponsibilityAbstract.Builder()
                            .add(new CheckWfEmpty())
                            .add(new CheckNodeIntegrity()).build();
            check.start(workflowExecuteEntity).check();
        }

    }

    /**
     * Check wf empty
     *
     * @description:
     * @author: cyj
     * @date 2022 -08-15 15:54:49
     */
    public static class CheckWfEmpty extends NormalChainResponsibilityAbstract<WorkflowExecuteEntity> {

        @Override
        public ChainResponsibilityElementResult apply(WorkflowExecuteEntity workflowExecuteEntity) {
            if (CollectionUtils.isEmpty(workflowExecuteEntity.getWorkflowExecuteDtos())) {
                return ChainResponsibilityElementResult.builder().aClass(this.getClass()).message(ErrorMessage.ERROR4).pass(false).build();
            }
            return ChainResponsibilityElementResult.builder().aClass(this.getClass()).pass(true).build();
        }
    }

    /**
     * Check active node
     *
     * @description:
     * @author: cyj
     * @date 2022 -08-15 15:54:49
     */
    public static class CheckActiveNode extends NormalChainResponsibilityAbstract<WorkflowExecuteEntity> {

        @Override
        public ChainResponsibilityElementResult apply(WorkflowExecuteEntity workflowExecuteEntity) {
            if (null == workflowExecuteEntity.getActiveNode()) {

                WorkflowExecuteDto endWorkflowExecuteDto =
                        workflowExecuteEntity.getWorkflowExecuteDtos().stream().filter(workflowExecutePo -> workflowExecutePo.getNodeType().equals(String.valueOf(WorkflowExecutePo.Type.end))).findAny().orElse(null);

                if (endWorkflowExecuteDto.getStatus().equals(WorkflowExecutePo.Status.COMPLATE.code())) {
                    return ChainResponsibilityElementResult.builder().aClass(this.getClass()).message(ErrorMessage.ERROR5).pass(false).build();
                }

                return ChainResponsibilityElementResult.builder().aClass(this.getClass()).message(ErrorMessage.ERROR6).pass(false).build();
            }
            return ChainResponsibilityElementResult.builder().aClass(this.getClass()).pass(true).build();
        }
    }

    /**
     * Check node integrity
     *
     * @description:
     * @author: cyj
     * @date 2022 -08-15 15:54:49
     */
    public static class CheckNodeIntegrity extends NormalChainResponsibilityAbstract<WorkflowExecuteEntity> {

        @Override
        public ChainResponsibilityElementResult apply(WorkflowExecuteEntity workflowExecuteEntity) {
            long endWorkflowExecuteCount =
                    workflowExecuteEntity.getWorkflowExecuteDtos().stream().filter(workflowExecutePo -> workflowExecutePo.getNodeType().equals(String.valueOf(WorkflowExecutePo.Type.end))).count();

            long startWorkflowExecuteCount =
                    workflowExecuteEntity.getWorkflowExecuteDtos().stream().filter(workflowExecutePo -> workflowExecutePo.getNodeType().equals(String.valueOf(WorkflowExecutePo.Type.start))).count();

            if (0 == endWorkflowExecuteCount) {
                return ChainResponsibilityElementResult.builder().aClass(this.getClass()).message(ErrorMessage.ERROR7).pass(false).build();
            }

            if (1 != endWorkflowExecuteCount) {
                return ChainResponsibilityElementResult.builder().aClass(this.getClass()).message(ErrorMessage.ERROR8).pass(false).build();
            }

            if (0 == startWorkflowExecuteCount) {
                return ChainResponsibilityElementResult.builder().aClass(this.getClass()).message(ErrorMessage.ERROR9).pass(false).build();
            }

            if (1 != startWorkflowExecuteCount) {
                return ChainResponsibilityElementResult.builder().aClass(this.getClass()).message(ErrorMessage.ERROR10).pass(false).build();
            }
            return ChainResponsibilityElementResult.builder().aClass(this.getClass()).pass(true).build();
        }
    }

}
